﻿using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Entity;
using System.Linq;

using System.IO;
using System.Security.AccessControl;
using System.Web;
using System.Web.Script.Serialization;
using DataModel;
using System.Drawing;
using System.Drawing.Imaging;
using System.Web.Hosting;
using System.Diagnostics;

namespace SocialPhotos {
	public class Upload : IHttpHandler {
        SocialPhotoModelContainer model = new SocialPhotoModelContainer();
        private readonly JavaScriptSerializer js = new JavaScriptSerializer();

		public class FilesStatus {
			public string thumbnail_url { get; set; }
			public string name { get; set; }
			public string url { get; set; }
			public int size { get; set; }
			public string type { get; set; }
			public string delete_url { get; set; }
			public string delete_type { get; set; }
			public string error { get; set; }
			public string progress { get; set; }
		}
		
		public bool IsReusable { get { return false; } }
		public void ProcessRequest (HttpContext context) {
			var r = context.Response;
			//ingestPath = @"C:\temp\ingest\";

			r.AddHeader("Pragma", "no-cache");
			r.AddHeader("Cache-Control", "private, no-cache");

			HandleMethod(context);
		}

		private void HandleMethod (HttpContext context) {
			switch (context.Request.HttpMethod) {
				case "HEAD":
				case "GET":
					string action = context.Request["action"];
                    if(action == "delete")
                        DeleteFile(context);
                    else 
					    ServeFile(context);
					break;

				case "POST":
					UploadFile(context);
					break;

				case "DELETE":
					DeleteFile(context);
					break;

				default:
					context.Response.ClearHeaders();
					context.Response.StatusCode = 405;
					break;
			}
		}

        private void DeleteFile(HttpContext context)
        {
            try
            {
                string filePath = HostingEnvironment.MapPath("~/App_Data/") + context.Request["f"];
                if (File.Exists(filePath))
                {
                    File.Delete(filePath);
                }

                filePath = HostingEnvironment.MapPath("~/App_Data/Thumbnail/") + context.Request["f"];
                if (File.Exists(filePath))
                {
                    File.Delete(filePath);
                }

                string strId = context.Request["id"];
                int id = Int32.Parse(strId);
                Photo photo = model.Photos.Single(p => p.Id == id);
                model.Photos.DeleteObject(photo);
                model.SaveChanges();
            }
            catch (Exception ex)
            {
                //use log4.net
            }
        }

		private void UploadFile (HttpContext context) {
			var statuses = new List<FilesStatus>();
			var headers = context.Request.Headers;

			if (string.IsNullOrEmpty(headers["X-File-Name"])) {
				UploadWholeFile(context, statuses);
			} else {
				UploadPartialFile(headers["X-File-Name"], context, statuses);
			}
           
			WriteJsonIframeSafe(context, statuses);
		}

        private void CreateThumbnail(string originalFile, int w, int h)
        {
            Image image = null;
            try
            {
                image = Image.FromFile(originalFile);
            }
            catch (Exception ex)
            {
                //Tong vao trong EventLog
            }
            float tyle = (float)image.Width / image.Height;
            if (tyle <= 0)
            {

                h = w;
                w = Convert.ToInt32(w * tyle);
            }
            else
            {
                h = Convert.ToInt32(w / tyle);
            }
            Image newImage = image.GetThumbnailImage(w, h, null, IntPtr.Zero);
            
            string saveFileLocation = HostingEnvironment.MapPath("~/App_Data/Thumbnail/") + 
                                Path.GetFileName(originalFile);

            string extentionFile = originalFile.Substring(originalFile.IndexOf(".") + 1);
            ImageFormat format = new ImageFormat(new Guid());
            if (extentionFile == "jpg")
            {
                format = ImageFormat.Jpeg;
            }
            else if (extentionFile == "bmp")
            {
                format = ImageFormat.Bmp;
            }
            else if (extentionFile == "gif")
            {
                format = ImageFormat.Gif;
            }
            else if (extentionFile == "png")
            {
                format = ImageFormat.Png;
            }
            newImage.Save(saveFileLocation, format);
            image.Dispose();
        }
        
		private void UploadPartialFile (string originalFileName, HttpContext context, List<FilesStatus> statuses) {
			if (context.Request.Files.Count != 1) throw new HttpRequestValidationException("Attempt to upload chunked file containing more than one fragment per request");

            var inputStream = context.Request.Files[0].InputStream;

            string extenstionFileName = originalFileName.Substring(originalFileName.IndexOf(".")+1);
            string savedFileName = string.Format("{0}.{1}", Guid.NewGuid(), extenstionFileName);
            string fullSaveFilePath = HostingEnvironment.MapPath("~/App_Data/") + savedFileName;

			using (var fs = new FileStream(fullSaveFilePath, FileMode.Append, FileAccess.Write)) {
				var buffer = new byte[1024];

				var l = inputStream.Read(buffer, 0, 1024);
				while (l > 0) {
					fs.Write(buffer,0,l);
					l = inputStream.Read(buffer, 0, 1024);
				}
				fs.Flush();
				fs.Close();
			}

			statuses.Add(new FilesStatus {
				thumbnail_url = "Thumbnail.ashx?f=" + savedFileName,
				url = "Upload.ashx?f=" + savedFileName,
				name = savedFileName,
				size = (int)(new FileInfo(savedFileName)).Length,
				type = "image/png",
				delete_url = "Upload.ashx?f=" + savedFileName,
				delete_type = "DELETE",
				progress = "1.0"
			});

            #region Use EF4 to save to db
            User user1 = model.Users.First();
            Photo newPhoto = new Photo
            {
                OriginalFileName = originalFileName,
                FileName = savedFileName,
                Likes = 0,
                Dislikes = 0,
                ContentType = "image",
                User = user1,
                Title = "",
                UploadTime = System.DateTime.Now,
            };
            try
            {
                object longitude = context.Request.Form["Longitude"];
                if (longitude != null) newPhoto.Longitude = double.Parse((string)longitude);
            }
            catch { }
            try
            {
                object latitude = context.Request.Form["Latitude"];
                if (latitude != null) newPhoto.Latitude = double.Parse((string)latitude);
            }
            catch { }
            #endregion
            if (CheckExtensionFileIsImage(extenstionFileName))
                CreateThumbnail(fullSaveFilePath, 150, 90);
		}

        private bool CheckExtensionFileIsImage(string extension)
        {
            if (extension.ToLower() == "jpg" || extension.ToLower() == "png"
               || extension.ToLower() == "bmp" || extension.ToLower() == "png"
               || extension.ToLower() == "tif" || extension.ToLower() == "gif")
                return true;
            else
                return false;
        }

		private void UploadWholeFile(HttpContext context, List<FilesStatus> statuses) {
			for (int i = 0; i < context.Request.Files.Count; i++) {
                var file = context.Request.Files[i];

                string originalFileName = Path.GetFileName(file.FileName);
                string extenstionFileName = originalFileName.Substring(originalFileName.LastIndexOf(".") + 1);
                string savedFileName = string.Format("{0}.{1}", Guid.NewGuid(), extenstionFileName);
                string fullSaveFilePath = HostingEnvironment.MapPath("~/App_Data/") + savedFileName;

                file.SaveAs(fullSaveFilePath);

                if (CheckExtensionFileIsImage(extenstionFileName))
                    CreateThumbnail(fullSaveFilePath, 150, 90);

                #region Use EF4 to save to db
                User user1 = model.Users.First();
                Photo newPhoto = new Photo
                    {
                        OriginalFileName = originalFileName,
                        FileName = savedFileName,
                        Likes = 0,
                        Dislikes = 0,
                        ContentType = "image",
                        User = user1,
                        Title = "",
                        UploadTime = System.DateTime.Now
                    };
                try
                {
                    object longitude = context.Request.Form["Longitude"];
                    if (longitude != null) newPhoto.Longitude = double.Parse((string)longitude);
                }
                catch { }
                try
                {
                    object latitude = context.Request.Form["Latitude"];
                    if (latitude != null) newPhoto.Latitude = double.Parse((string)latitude);
                }
                catch { }
                #endregion


                model.AddToPhotos(newPhoto);
                model.SaveChanges();

                statuses.Add(new FilesStatus
                {
                    thumbnail_url = "Thumbnail.ashx?f=" + savedFileName,
                    url = "Upload.ashx?f=" + savedFileName,
                    name = savedFileName,
                    size = file.ContentLength,
                    type = "image/png",
                    delete_url = string.Format("Upload.ashx?action=delete&f={0}&id={1}", newPhoto.FileName, newPhoto.Id),
                    delete_type = "GET",
                    progress = "1.0"
                });
			}
		}

		private void WriteJsonIframeSafe(HttpContext context, List<FilesStatus> statuses) {
			context.Response.AddHeader("Vary", "Accept");
			try {
				if (context.Request["HTTP_ACCEPT"].Contains("application/json")) {
					context.Response.ContentType = "application/json";
				} else {
					context.Response.ContentType = "text/plain";
				}
			} catch {
				context.Response.ContentType = "text/plain";
			}

			var jsonObj = js.Serialize(statuses.ToArray());
			context.Response.Write(jsonObj);
		}

		private void ServeFile (HttpContext context) {
			if (string.IsNullOrEmpty(context.Request["f"])) ListCurrentFiles(context);
			else DeliverFile(context);
		}

		private void DeliverFile (HttpContext context) {
            var filePath = HostingEnvironment.MapPath("~/App_Data/") + context.Request["f"];
			if (File.Exists(filePath)) {
                string fileName = Path.GetFileName(filePath);
                string extension = fileName.Substring(fileName.IndexOf(".")+1);
                if (extension == "jpg" || extension == "png")
                {
                    context.Response.ContentType = "image/jpeg";
                    context.Response.WriteFile(filePath);
                }
                else
                {
                    context.Response.ContentType = "application/octet-stream";
                    context.Response.WriteFile(filePath);
                    context.Response.AddHeader("Content-Disposition", "attachment, filename=\"" + context.Request["f"] + "\"");
                }
				
			} else {
				context.Response.StatusCode = 404;
			}
		}

		private void ListCurrentFiles (HttpContext context) {
            var files = new List<FilesStatus>();
            foreach (Photo p in model.Photos)
            {
                files.Add(new FilesStatus
                {
                    thumbnail_url = "Thumbnail.ashx?f=" + p.FileName,
                    url = "Upload.ashx?f=" + p.FileName,
                    name = p.FileName,
                    size = (int)111,
                    type = "image/png",
                    delete_url = string.Format("Upload.ashx?action=delete&f={0}&id={1}", p.FileName, p.Id),
                    delete_type = "GET"
                });
            }

			context.Response.AddHeader("Content-Disposition", "inline, filename=\"files.json\"");
			var jsonObj = js.Serialize(files.ToArray());
			context.Response.Write(jsonObj);
			context.Response.ContentType = "application/json";
		}
	}
}
